package org.wm.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.wm.constant.ScheduleConstants;
import org.wm.domain.SysJob;
import org.wm.exception.job.TaskException;
import org.wm.mapper.SysJobMapper;
import org.wm.service.ISysJobService;
import org.wm.util.CronUtils;
import org.wm.util.ScheduleUtils;

import java.util.Arrays;
import java.util.List;

/**
 * 定时任务调度信息 服务层
 * (已按照 Mybatis-Plus 混合模式进行改造)
 */
@RequiredArgsConstructor
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements ISysJobService {

    private final Scheduler scheduler;

    @PostConstruct
    public void init() throws SchedulerException, TaskException {
        scheduler.clear();
        List<SysJob> jobList = this.list();
        for (SysJob job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }

    @Override
    public IPage<SysJob> selectJobPage(Page<SysJob> page, SysJob job) {
        return baseMapper.selectJobList(page, job);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean pauseJob(SysJob job) throws SchedulerException {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        boolean success = this.updateById(job);
        if (success) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(job.getJobId(), job.getJobGroup()));
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resumeJob(SysJob job) throws SchedulerException {
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        boolean success = this.updateById(job);
        if (success) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(job.getJobId(), job.getJobGroup()));
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteJob(SysJob job) throws SchedulerException {
        boolean success = this.removeById(job.getJobId());
        if (success) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(job.getJobId(), job.getJobGroup()));
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
        List<SysJob> jobsToDelete = this.listByIds(Arrays.asList(jobIds));
        for (SysJob job : jobsToDelete) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(job.getJobId(), job.getJobGroup()));
        }
        this.removeByIds(Arrays.asList(jobIds));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeStatus(SysJob job) throws SchedulerException {
        String status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            return resumeJob(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            return pauseJob(job);
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void run(SysJob job) throws SchedulerException {
        SysJob properties = this.getById(job.getJobId());
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        scheduler.triggerJob(ScheduleUtils.getJobKey(job.getJobId(), job.getJobGroup()), dataMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertJob(SysJob job) throws SchedulerException, TaskException {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        boolean success = this.save(job);
        if (success) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateJob(SysJob job) throws SchedulerException, TaskException {
        SysJob properties = this.getById(job.getJobId());
        boolean success = this.updateById(job);
        if (success) {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return success;
    }

    private void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
        JobKey jobKey = ScheduleUtils.getJobKey(job.getJobId(), jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

    @Override
    public boolean checkCronExpressionIsValid(String cronExpression) {
        return CronUtils.isValid(cronExpression);
    }
}